package com.example.ramcache.schema;

import com.example.ramcache.IEntity;
import com.example.ramcache.anno.Cached;
import com.example.ramcache.anno.Persister;
import com.example.ramcache.aop.LockAspect;
import com.example.ramcache.exception.ConfigurationException;
import com.example.ramcache.orm.Accessor;
import com.example.ramcache.orm.Querier;
import com.example.ramcache.orm.impl.HibernateAccessor;
import com.example.ramcache.orm.impl.HibernateQuerier;
import com.example.ramcache.persist.PersisterConfig;
import com.example.ramcache.persist.PersisterType;
import com.example.ramcache.properties.PersisterProperties;
import com.example.ramcache.properties.RamcacheProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.helpers.FormattingTuple;
import org.slf4j.helpers.MessageFormatter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;
import org.springframework.util.SystemPropertyUtils;

import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * @author frank
 * @date 2019-03-22 18:14
 * @desc
 **/
@Configuration
@EnableConfigurationProperties({RamcacheProperties.class})
public class RamchaceAutoConfiguration implements ImportAware {
    private static final Logger logger = LoggerFactory.getLogger(RamchaceAutoConfiguration.class);

    @Autowired
    private RamcacheProperties ramcacheProperties;
    private AnnotationMetadata importMetadata;

    /** 默认资源匹配符 */
    protected static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
    /** 资源搜索分析器，由它来负责检索EAO接口 */
    private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
    /** 类的元数据读取器，由它来负责读取类上的注释信息 */
    private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver);

    @Bean
    public InjectProcessor registerInjectProcess() {
        return new InjectProcessor();
    }


    @Bean
    public LockAspect registerLockAspect() {
        return new LockAspect();
    }

    @Bean
    public Accessor registerAccessor() {
        HibernateAccessor accessor = new HibernateAccessor();
        return accessor;
    }

    @Bean
    public Querier registerQuerier() {
        Querier querier = new HibernateQuerier();
        return querier;
    }

    @Bean
    public ServiceManagerFactory registerServiceManagerFactory() {
        ServiceManagerFactory serviceManagerFactory = new ServiceManagerFactory();
        serviceManagerFactory.setShutdownDelay(ramcacheProperties.getShutdownDelay());
        serviceManagerFactory.setAccessor(registerAccessor());
        serviceManagerFactory.setQuerier(registerQuerier());
        serviceManagerFactory.setConstants(ramcacheProperties.getConstants());
        // 设置持久化处理器配置
        Map<String, PersisterConfig> persisterConfigs = new HashMap<>();
        //默认
        persisterConfigs.put(Persister.DEFAULT, new PersisterConfig(PersisterType.QUEUE, "-1:true"));
        for (PersisterProperties properties : ramcacheProperties.getPersisters()) {
            String name = properties.getName();
            PersisterType type = properties.getType();
            String value = properties.getConfig();
            persisterConfigs.put(name, new PersisterConfig(type, value));
        }
        serviceManagerFactory.setPersisterConfig(persisterConfigs);
        String packages = ClassUtils.getPackageName(importMetadata.getClassName());
        String[] names = getResources(packages);
        // 设置实体集合
        Set<Class<? extends IEntity<?>>> classes = new HashSet<>();
        for (String resource : names) {
            Class<? extends IEntity<?>> clz = null;
            try {
                clz = (Class<? extends IEntity<?>>) Class.forName(resource);
            } catch (ClassNotFoundException e) {
                FormattingTuple message = MessageFormatter.format("无法获取的资源类[{}]", resource);
                logger.error(message.getMessage());
                throw new ConfigurationException(message.getMessage(), e);
            }
            classes.add(clz);
        }
        serviceManagerFactory.setEntityClasses(classes);
        return serviceManagerFactory;
    }

    /**
     * 获取指定包下的静态资源对象
     * @param packageName 包名
     * @return
     * @throws IOException
     */
    private String[] getResources(String packageName) {
        try {
            // 搜索资源
            String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
                    + resolveBasePackage(packageName) + "/" + DEFAULT_RESOURCE_PATTERN;
            Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
            // 提取资源
            Set<String> result = new HashSet<String>();
            String name = Cached.class.getName();
            for (Resource resource : resources) {
                if (!resource.isReadable()) {
                    continue;
                }
                // 判断是否静态资源
                MetadataReader metaReader = this.metadataReaderFactory.getMetadataReader(resource);
                AnnotationMetadata annoMeta = metaReader.getAnnotationMetadata();
                if (!annoMeta.hasAnnotation(name)) {
                    continue;
                }
                ClassMetadata clzMeta = metaReader.getClassMetadata();
                result.add(clzMeta.getClassName());
            }
            return result.toArray(new String[0]);
        } catch (IOException e) {
            String message = "无法读取资源信息";
            logger.error(message, e);
            throw new ConfigurationException(message, e);
        }
    }

    protected String resolveBasePackage(String basePackage) {
        return ClassUtils.convertClassNameToResourcePath(SystemPropertyUtils.resolvePlaceholders(basePackage));
    }

    @Override
    public void setImportMetadata(AnnotationMetadata importMetadata) {
        this.importMetadata = importMetadata;
    }
}
